#include "DXUT.h"
#include "ServerMainThread.h"

#ifdef SERVER

#pragma comment(lib, "ws2_32.lib")

ServerClientThread::ServerClientThread( ServerWorld* pWorld, ServerMainThread* pCon, SOCKET socket) {
	m_pWorld		= pWorld;
	m_Terminate		= false;
	m_init			= false;
	m_Socket		= socket;
	m_pGlobalCon	= pCon;

	m_Thread_Handle = CreateThread( NULL, 0, run, this, 0, NULL); 
}

ServerClientThread::~ServerClientThread() {
	if (m_init) {
		m_pGlobalCon->Remove(m_ID);
		m_pWorld->disconnectPlayer(m_ID);
	}
}

ServerMainThread::ServerMainThread( ServerWorld* pWorld) {
	InitializeSRWLock(&m_LOCK);
	m_CurID			= 1;
	m_pWorld		= pWorld;
	m_Terminate		= false;

	m_Thread_Handle = CreateThread( NULL, 0, run, this, 0, NULL); 
}

void ServerClientThread::init(int ID) {
	if (!m_init) {
		m_init	= true;
		m_ID	= ID;
		m_pWorld->connectPlayer(ID);
	}
};

void ServerClientThread::Dispatch(BYTE * memory, int size) {
	Protostruct Proto; 
	memcpy(&Proto, memory, sizeof(Protostruct));
	memory += sizeof(Protostruct);
	size   -= sizeof(Protostruct);
	if (size < Proto.Size) {
		// Problem
	} else {
		switch (Proto.Type)
		{
		case -1: /* CLOSE */
			cerr << "Client closed!\n";
			m_Terminate		= true;
			shutdown(m_Socket, 0);
			closesocket(m_Socket);
			break;
		case 0: /* TEXT */
			printf((char*)memory);
			printf("\n");
			break;
		case 51: /* SET POSIION */ {
			D3DXVECTOR3 vec; memcpy(&vec, memory, sizeof(D3DXVECTOR3));
			m_pWorld->setPlayerPosition(vec, m_ID);
				 } break;
		case 100: /* GET CHUNK */ {
			int x; memcpy(&x, memory, 4); memory += 4;
			int z; memcpy(&z, memory, 4); memory += 4;
			BYTE * data; uLong uSize;
			if (m_pWorld->getCompressedCluster(x, z, &data, &uSize)) {
				/* Create Protocolstruct */
				Proto.Type = 101;
				Proto.Size = uSize + sizeof(Protostruct) + 8;

				/* copy memory over */
				BYTE * sendData = (BYTE*)malloc(Proto.Size);
				BYTE * sendDataBuffer = sendData;
				memcpy(sendDataBuffer, &Proto, sizeof(Protostruct)); sendDataBuffer += sizeof(Protostruct);
				memcpy(sendDataBuffer, &x, 4); sendDataBuffer += 4;
				memcpy(sendDataBuffer, &z, 4); sendDataBuffer += 4;
				memcpy(sendDataBuffer, data, uSize);

				/* send */
				Send(sendData, Proto.Size);

				/* free memory */
				free(data);
				free(sendData);
			} else {
				cerr << "Failed to get chunk!\n";
			}
				  } break;
		case 200: /* BLOCK PLACEMENT */ {
			INTVECTOR3  pos;   memcpy(&pos, memory, sizeof(INTVECTOR3)); memory += sizeof(INTVECTOR3);
			Block		block; memcpy(&block, memory, sizeof(Block));
			m_pWorld->placeBlock(pos.x, pos.z, pos.y, block);

			/* copy memory over */
			BYTE * sendData = (BYTE*)malloc(Proto.Size);
			BYTE * sendDataBuffer = sendData;
			memcpy(sendDataBuffer, &Proto, sizeof(Protostruct));	sendDataBuffer += sizeof(Protostruct);
			memcpy(sendDataBuffer, &pos, sizeof(INTVECTOR3));		sendDataBuffer += sizeof(INTVECTOR3);
			memcpy(sendDataBuffer, &block, sizeof(Block));			sendDataBuffer += sizeof(Block);

			SendExcept(sendDataBuffer, Proto.Size);
				  } break;
		case 202: /* BLOCK BREAK */ {
			INTVECTOR3  pos;   memcpy(&pos, memory, sizeof(INTVECTOR3)); memory += sizeof(INTVECTOR3);
			m_pWorld->breakBlock(pos.x, pos.z, pos.y);

			/* copy memory over */
			BYTE * sendData = (BYTE*)malloc(Proto.Size);
			BYTE * sendDataBuffer = sendData;
			memcpy(sendDataBuffer, &Proto, sizeof(Protostruct));	sendDataBuffer += sizeof(Protostruct);
			memcpy(sendDataBuffer, &pos, sizeof(INTVECTOR3));		sendDataBuffer += sizeof(INTVECTOR3);

			SendExcept(sendDataBuffer, Proto.Size);
				  } break;
		default:
			break;
		}
	}
};

int ServerClientThread::Send(BYTE * memory, int size) {
	return send(m_Socket, (char*)memory, size, 0);
};

int ServerClientThread::SendExcept(BYTE * memory, int size) {
	return m_pGlobalCon->SendExcept(m_ID, memory, size);
};

int ServerClientThread::Send(Protostruct proto, BYTE * memory, int size) {
	proto.Size = sizeof(Protostruct) + size;
	BYTE * sendData = (BYTE*)malloc(proto.Size);
	BYTE * sendDataBuffer = sendData;
	memcpy(sendDataBuffer, &proto, sizeof(Protostruct)); sendDataBuffer += sizeof(Protostruct);
	if (memory) memcpy(sendDataBuffer, memory, size);
	int result = send(m_Socket, (char*)sendData, proto.Size, 0);
	free(sendData);
	return result;
};

int ServerClientThread::SendExcept(Protostruct proto, BYTE * memory, int size) {
	proto.Size = sizeof(Protostruct) + size;
	BYTE * sendData = (BYTE*)malloc(proto.Size);
	BYTE * sendDataBuffer = sendData;
	memcpy(sendDataBuffer, &proto, sizeof(Protostruct)); sendDataBuffer += sizeof(Protostruct);
	if (memory) memcpy(sendDataBuffer, memory, size);
	int result = SendExcept(sendData, proto.Size);
	free(sendData);
	return result;
};

int ServerClientThread::Send(Protostruct proto, pair<BYTE*,int> * memory, int count) {
	int size = sizeof(Protostruct);
	for (int i = 0; i < count; i++) {
		size += memory[i].second;
	}
	proto.Size = size;
	BYTE * sendData = (BYTE*)malloc(size);
	BYTE * sendDataBuffer = sendData;
	memcpy(sendDataBuffer, &proto, sizeof(Protostruct)); sendDataBuffer += sizeof(Protostruct);
	for (int i = 0; i < count; i++) {
		memcpy(sendDataBuffer, memory[i].first, memory[i].second); sendDataBuffer += memory[i].second;
	}
	int result = send(m_Socket, (char*)sendData, proto.Size, 0);
	free(sendData);
	return result;
};

int ServerClientThread::SendExcept(Protostruct proto, pair<BYTE*,int> * memory, int count) {
	int size = sizeof(Protostruct);
	for (int i = 0; i < count; i++) {
		size += memory[i].second;
	}
	proto.Size = size;
	BYTE * sendData = (BYTE*)malloc(size);
	BYTE * sendDataBuffer = sendData;
	memcpy(sendDataBuffer, &proto, sizeof(Protostruct)); sendDataBuffer += sizeof(Protostruct);
	for (int i = 0; i < count; i++) {
		memcpy(sendDataBuffer, memory[i].first, memory[i].second); sendDataBuffer += memory[i].second;
	}
	int result = SendExcept(sendData, proto.Size);
	free(sendData);
	return result;
};

void ServerClientThread::Terminate() {
	m_Terminate		= true;
	shutdown(m_Socket, 0);
	closesocket(m_Socket);
	WaitForSingleObject(m_Thread_Handle, 1000);
};

DWORD WINAPI ServerClientThread::run( LPVOID pParam ) {
	ServerClientThread* This = (ServerClientThread*) pParam;
	if ( This == NULL ) return 1; 

	SOCKET        sock = This->m_Socket;
	char          szBuff[DEFAULT_BUFFER];
	int           ret;

	while(!This->m_Terminate) {
		// Perform a blocking recv() call
		//
		ret = recv(sock, szBuff, DEFAULT_BUFFER, 0);
		if (ret == 0)        // Graceful close
			break;
		else if (ret == SOCKET_ERROR) {
			printf("recv() failed: %d\n", WSAGetLastError());
			break;
		}
		//szBuff[ret] = '\0';
		//printf("RECV: '%s'\n", szBuff);
		This->Dispatch((BYTE*)szBuff, DEFAULT_BUFFER);
	}
	delete This;
	return 0;
};

DWORD WINAPI ServerMainThread::run( LPVOID pParam ) {
	ServerMainThread* This = (ServerMainThread*) pParam;
	if ( This == NULL ) return 1; 

	WSADATA			wsd;
	SOCKET			sListen, sClient;
	sockaddr_in		local, client;
	int				iAddrSize;

	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
		printf("Failed to load Winsock!\n");
		return 1;
	}

	sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
	if (sListen == SOCKET_ERROR) {
		printf("socket() failed: %d\n", WSAGetLastError());
		return 1;
	}

	local.sin_addr.s_addr	= htonl(INADDR_ANY);
	local.sin_family		= AF_INET;
	local.sin_port			= htons(DEFAULT_PORT);

	if (bind(sListen, (struct sockaddr *)&local, sizeof(local)) == SOCKET_ERROR) {
		printf("bind() failed: %d\n", WSAGetLastError());
		return 1;
	}
	listen(sListen, SOMAXCONN);

	while(!This->m_Terminate) {
		iAddrSize = sizeof(client);
		sClient = accept(sListen, (struct sockaddr *)&client, &iAddrSize);
		if (sClient == INVALID_SOCKET) {
			printf("accept() failed: %d\n", WSAGetLastError());
			continue;
		}
		printf("Accepted client: %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
		This->Insert(new ServerClientThread(This->m_pWorld, This, sClient));		
	}
	closesocket(sListen);
	WSACleanup();
	delete This;
	return 0;
};

int ServerMainThread::Insert(ServerClientThread * client) {
	AcquireSRWLockExclusive(&m_LOCK);
	m_Clients.insert(pair<int, ServerClientThread*>(m_CurID, client));
	int result = m_CurID++;
	client->init(result);
	ReleaseSRWLockExclusive(&m_LOCK);
	return result;
};

void ServerMainThread::Remove(int id) {
	AcquireSRWLockExclusive(&m_LOCK);
	m_Clients.erase(id);
	ReleaseSRWLockExclusive(&m_LOCK);
};

int ServerMainThread::Send(int ID, BYTE * memory, int size) {
	int result = 0;
	auto it = m_Clients.find(ID);
	if (it != m_Clients.end()) {
		it->second->Send(memory, size);
	}
	return result;
};

int ServerMainThread::SendExcept(int ID, BYTE * memory, int size) {
	int result = 0;
	auto fit = m_Clients.find(ID);
	for (auto it = m_Clients.begin(); it != m_Clients.end(); it++)
		if (it != fit) {
			it->second->Send(memory, size);
		}
		return result;
};

int ServerMainThread::SendAll(BYTE * memory, int size) {
	int result = 0;
	for (auto it = m_Clients.begin(); it != m_Clients.end(); it++)
		it->second->Send(memory, size);
	return result;
};

int ServerMainThread::Send(int ID, Protostruct proto, BYTE * memory, int size) {
	int result = 0;
	auto it = m_Clients.find(ID);
	if (it != m_Clients.end()) {
		it->second->Send(proto, memory, size);
	}
	return result;
};

int ServerMainThread::SendExcept(int ID, Protostruct proto, BYTE * memory, int size) {
	int result = 0;
	auto fit = m_Clients.find(ID);
	for (auto it = m_Clients.begin(); it != m_Clients.end(); it++)
		if (it != fit) {
			it->second->Send(proto, memory, size);
		}
		return result;
};

int ServerMainThread::SendAll(Protostruct proto, BYTE * memory, int size) {
	int result = 0;
	for (auto it = m_Clients.begin(); it != m_Clients.end(); it++)
		it->second->Send(proto, memory, size);
	return result;
};

int ServerMainThread::Send(int ID, Protostruct proto, pair<BYTE*,int> * memory, int count) {
	int result = 0;
	auto it = m_Clients.find(ID);
	if (it != m_Clients.end()) {
		it->second->Send(proto, memory, count);
	}
	return result;
};

int ServerMainThread::SendExcept(int ID, Protostruct proto, pair<BYTE*,int> * memory, int count) {
	int result = 0;
	auto fit = m_Clients.find(ID);
	for (auto it = m_Clients.begin(); it != m_Clients.end(); it++)
		if (it != fit) {
			it->second->Send(proto, memory, count);
		}
		return result;
};

int ServerMainThread::SendAll(Protostruct proto, pair<BYTE*,int> * memory, int count) {
	int result = 0;
	for (auto it = m_Clients.begin(); it != m_Clients.end(); it++)
		it->second->Send(proto, memory, count);
	return result;
};

#endif /* SERVER */